package org.io4j.resource;

import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.nio.charset.Charset;
import java.util.List;

import org.io4j.InputOutputException;
import org.io4j.data.Streams;
import org.io4j.filesystem.File;
import org.io4j.filesystem.Path;
import org.io4j.util.Argument;

abstract class BaseResource implements Resource {

	private static final String URL_PROTOCOL_FILE = "file";

	public byte[] readBytes() {
		return Streams.transferAndClose().from(openInputStream()).toByteArray();
	}

	public List<String> readLines() {
		return Streams.transferAndClose().from(openInputStream()).toLineList();
	}

	public List<String> readLines(Charset charset) {
		return Streams.transferAndClose().from(openInputStream()).usingCharset(charset).toLineList();
	}

	public String readText() {
		return Streams.transferAndClose().from(openInputStream()).toString();
	}

	public String readText(Charset charset) {
		return Streams.transferAndClose().from(openInputStream()).usingCharset(charset).toString();
	}

	public abstract InputStream openInputStream();

	public File copy(Path destinationPath) {
		Argument.isNotNull(destinationPath, "Destination path is null");
		File file = File.create(destinationPath);
		Streams.transferAndClose().from(openInputStream()).to(file.openOutputStream());
		return file;
	}

	public abstract URL getUrl();

	public abstract String getName();

	public long getContentLength() {
		if (resolvesToFile()) {
			return getFile().getSize();
		} else {
			try {
				return openConnection().getContentLength();
			} catch (InputOutputException e) {
				return 0;
			}
		}
	}

	private boolean resolvesToFile() {
		try {
			return URL_PROTOCOL_FILE.equals(getUrl().getProtocol());
		} catch (ResourceResolutionException e) {
			return false;
		}
	}

	private File getFile() {
		return new File(new Path(getUrl()));
	}

	private URLConnection openConnection() {
		URL url = getUrl();
		try {
			URLConnection connection = url.openConnection();
			connection.setUseCaches(false);
			if (connection instanceof HttpURLConnection) {
				((HttpURLConnection) connection).setRequestMethod("HEAD");
			}
			return connection;
		} catch (IOException e) {
			throw new InputOutputException("Could not establish connection to " + url.toString(), e);
		}
	}

	public long getModificationTimestamp() {
		if (resolvesToFile()) {
			return getFile().getModificationTimestamp();
		} else {
			try {
				return openConnection().getLastModified();
			} catch (InputOutputException e) {
				return 0;
			}
		}
	}

	/**
	 * Returns the string representation of this resource (based on its path or
	 * name).
	 */
	@Override
	public final String toString() {
		return getName();
	}
}
